/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "dataTools.h"
#include "controller.h"
#include "graph.h"
#include <assert.h>
#include <sstream>
using namespace std;

// TODO:
// Change property type from node,edge to: filterNode, filterEdge
// 


class FilterNodesValue:public AnalysisTool {
public:
	FilterNodesValue(Controller *controller):AnalysisTool("Filter","Filter Nodes by Value",controller) {
		setPropertyStringList("Property", "", "node");
		setPropertyFloat("Value", 0.0);
		setPropertyStringList("Operation", "Greater than", "operation");
		setPropertyString("Result", "result");
	}
	void compute() {
		assert(controller);
		Graph *graph = controller->getGraph("graph");
		
		if(listener) listener->setProgress(0);
		string property = getPropertyString("Property");
		string operation = getPropertyString("Operation");
		string resultProp = getPropertyString("Result");
		float value = getPropertyFloat("Value");
		
		int i = 0;
		ElementIterator *it = graph->createNodeIterator();
		for(it->begin();it->hasMoreElements();) {
			Node *node = (Node*) it->next();
			float v = graph->getAsFloat(node,	property, 0, false);
			bool result = false;
			if(operation=="Greater than") result = v>value;
			if(operation=="Greater or equal than") result = v>=value;
			if(operation=="Less than") result = v<value;
			if(operation=="Less or equal than") result = v<=value;
			if(operation=="Equals") result = v==value;
			if(operation=="Not equals") result = v!=value;
			if(listener) listener->setProgress((float) ++i/(float) graph->numNodes());
			node->setPropertyBool(resultProp, result);
		}
		graph->destroyIterator(it);
		graph->computeEdgeMinMaxFloat(resultProp);
		controller->addDataProperty("node", resultProp, PROPERTY_BOOL);
		if(listener) listener->done();
	}

};

class FilterNodesString:public AnalysisTool {
public:
	FilterNodesString(Controller *controller):AnalysisTool("Filter","Filter Nodes by Query",controller) {
		setPropertyStringList("Property", "", "node");
		setPropertyString("Query", "");
		setPropertyStringList("Operation", "Equals", "stringOperation");
		setPropertyString("Result", "result");
	}
	void compute() {
		assert(controller);
		Graph *graph = controller->getGraph("graph");
		
		if(listener) listener->setProgress(0);
		string property = getPropertyString("Property");
		string operation = getPropertyString("Operation");
		string resultProp = getPropertyString("Result");
		string query = getPropertyString("Query");
		
		int i = 0;
		ElementIterator *it = graph->createNodeIterator();
		for(it->begin();it->hasMoreElements();) {
			Node *node = (Node*) it->next();
			string v = graph->getAsString(node,property);
			bool result = false;
			int found = v.find(query);
			if(operation=="Equals") result = v==query;
			if(operation=="Contains") result = found!=(int) string::npos;
			if(operation=="Starts with") result = int(found)==0;
			if(operation=="Ends with") result = int(found)==(int) (v.length()-query.length());
			if(listener) listener->setProgress((float) ++i/(float) graph->numNodes());
			graph->nodes[i]->setPropertyBool(resultProp, result);
		}
		graph->destroyIterator(it);
		
		graph->computeEdgeMinMaxFloat(resultProp);
		controller->addDataProperty("node", resultProp, PROPERTY_BOOL);
		if(listener) listener->done();
	}
	
};

class FilterEdgesValue:public AnalysisTool {
public:
	FilterEdgesValue(Controller *controller):AnalysisTool("Filter","Filter Edges by Value",controller) {
		setPropertyStringList("Property", "", "edge");
		setPropertyFloat("Value", 0.0);
		setPropertyStringList("Operation", "Greater than", "operation");
		setPropertyString("Result", "result");
	}
	void compute() {
		assert(controller);
		Graph *graph = controller->getGraph("graph");
		
		if(listener) listener->setProgress(0);
		string property = getPropertyString("Property");
		string operation = getPropertyString("Operation");
		string resultProp = getPropertyString("Result");
		float value = getPropertyFloat("Value");
		
		ElementIterator *it = new EdgeIterator(graph);
		assert(it);
		int i =0;
		for(it->begin();it->hasMoreElements();) {
			Edge *edge = (Edge *) it->next();
			assert(edge);
			float v = graph->getAsFloat(edge,property, 0, false);
			bool result = false;
			if(operation=="Greater than") result = v>value;
			if(operation=="Greater or equal than") result = v>=value;
			if(operation=="Less than") result = v<value;
			if(operation=="Less or equal than") result = v<=value;
			if(operation=="Equals") result = v==value;
			if(operation=="Not equals") result = v!=value;
			if(listener) listener->setProgress((float) ++i/(float) graph->numNodes());
			edge->setPropertyBool(resultProp, result);
		}
		graph->destroyIterator(it);
		
		graph->computeEdgeMinMaxFloat(resultProp);
		controller->addDataProperty("edge", resultProp, PROPERTY_BOOL);
		if(listener) listener->done();
	}
	
};

class FilterEdgesString:public AnalysisTool {
public:
	FilterEdgesString(Controller *controller):AnalysisTool("Filter","Filter Edges by Query",controller) {
		setPropertyStringList("Property", "", "node");
		setPropertyString("Query", "");
		setPropertyStringList("Operation", "Equals", "stringOperation");
		setPropertyString("Result", "result");
	}
	void compute() {
		assert(controller);
		Graph *graph = controller->getGraph("graph");
		
		if(listener) listener->setProgress(0);
		string property = getPropertyString("Property");
		string operation = getPropertyString("Operation");
		string resultProp = getPropertyString("Result");
		string query = getPropertyString("Query");
		
		ElementIterator *it = new EdgeIterator(graph);
		assert(it);
		int i =0;
		for(it->begin();it->hasMoreElements();) {
			Edge *edge = (Edge *) it->next();
			assert(edge);
			string v = graph->getAsString(edge,property);
			bool result = false;
			int found = v.find(query);
			if(operation=="Equals") result = v==query;
			if(operation=="Contains") result = found!=(int) string::npos;
			if(operation=="Starts with") result = int(found)==0;
			if(operation=="Ends with") result = int(found)==(int) (v.length()-query.length());
			//if(listener) listener->setProgress((float) ++i/(float) graph->numEdges());
			edge->setPropertyBool(resultProp, result);
		}
		graph->destroyIterator(it);
		
		graph->computeEdgeMinMaxFloat(resultProp);
		controller->addDataProperty("edge", resultProp, PROPERTY_BOOL);
		if(listener) listener->done();
	}
	
};


class FilterBoolean:public AnalysisTool {
protected:
	string type;
public:
	FilterBoolean(const char *name, Controller *controller):AnalysisTool("Filter",name,controller) {
		setPropertyStringList("Operation", "AND", "boolean");
		setPropertyString("Result", "result");
	}
	virtual ElementIterator * getIterator() = 0; 

	virtual void compute() {
		assert(controller);
		Graph *graph = controller->getGraph("graph");
		
		if(listener) listener->setProgress(0);
		string filter1 = getPropertyString("Filter A");
		string filter2 = getPropertyString("Filter B");
		string operation = getPropertyString("Operation");
		string resultProp = getPropertyString("Result");
		float value = getPropertyFloat("Value");
		
		ElementIterator *it = getIterator();
		assert(it);
		int i =0;
		for(it->begin();it->hasMoreElements();) {
			PropertyBag *element = (PropertyBag *) it->next();
			assert(element);
			bool f1 = graph->getAsBoolean(element,filter1);
			bool f2 = graph->getAsBoolean(element,filter2);
			bool result = false;
			if(operation=="AND") result = f1 && f2;
			if(operation=="OR") result = f1 || f2;
			if(operation=="NOT") result = !f1;
			element->setPropertyBool(resultProp, result);
		}
		graph->destroyIterator(it);
		
		graph->computeEdgeMinMaxFloat(resultProp);
		controller->addDataProperty(type, resultProp, PROPERTY_BOOL);
		if(listener) listener->done();
	}
	
};

class FilterNodesBoolean:public FilterBoolean {
public:
	FilterNodesBoolean(Controller *controller):FilterBoolean("Compose Filter Nodes", controller) {
		setPropertyString("Filter A", "node");
		setPropertyString("Filter B", "node");
		type="node";
	}
	virtual ElementIterator * getIterator() {
		Graph *graph = controller->getGraph("graph");
		return new NodeIterator(graph);
	}
};

class FilterEdgesBoolean:public FilterBoolean {
public:
	FilterEdgesBoolean(Controller *controller):FilterBoolean("Compose Filter Edges", controller) {
		setPropertyString("Filter A", "edge");
		setPropertyString("Filter B", "edge");
		type="edge";
	}
	virtual ElementIterator * getIterator() {
		Graph *graph = controller->getGraph("graph");
		return new EdgeIterator(graph);
	}
	
};
void addFilterTools(Controller *controller) {
	controller->addTool(new FilterNodesValue(controller));
	controller->addTool(new FilterNodesString(controller));
	controller->addTool(new FilterEdgesValue(controller));
	controller->addTool(new FilterEdgesString(controller));
	controller->addTool(new FilterNodesBoolean(controller));
	controller->addTool(new FilterEdgesBoolean(controller));
}